package com.example.Windturbine.Calculation.TreeDOMethod1;

import com.example.Windturbine.Config.RedisConfig;
import com.example.Windturbine.Dao.ProductDOMapper;
import com.example.Windturbine.DataObject.ProductDO;
import com.example.Windturbine.DataObject.ProductInfoDO;
import com.example.Windturbine.DataObject.ProductUseDO;
import com.example.Windturbine.Error.BusinessException;
import com.example.Windturbine.Error.EmBusinessError;
import com.example.Windturbine.Service.ManufactureService;
import com.example.Windturbine.Service.MaterialService;
import com.example.Windturbine.Service.Model.ManufactureModel;
import com.example.Windturbine.Service.Model.MaterialModel;
import com.example.Windturbine.Service.Model.TransportModel;
import com.example.Windturbine.Service.ProductService;
import com.example.Windturbine.Service.TransportService;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Author 丁永新
 * @Date 2021/12/21
 */

@Data
@NoArgsConstructor
@Component
public class LCA1 {

    ProductDOMapper productDOMapper;


    ProductService productService;


    MaterialService materialService;


    TransportService transportService;


    ManufactureService manufactureService;


    RedisTemplate redisTemplate;


    ProductUseDO productUseDO; //使用阶段


    public LCA1(ProductDOMapper productDOMapper, ProductService productService, MaterialService materialService, TransportService transportService, ManufactureService manufactureService, ProductUseDO productUseDO) {
        this.productDOMapper = productDOMapper;
        this.productService = productService;
        this.materialService = materialService;
        this.transportService = transportService;
        this.manufactureService = manufactureService;
        this.productUseDO = productUseDO;
    }

    public LCA1(ProductUseDO productUseDO) {
        this.productUseDO = productUseDO;
    }

    public LCA1(ProductDOMapper productDOMapper, ProductService productService, MaterialService materialService, TransportService transportService, ManufactureService manufactureService, RedisTemplate redisTemplate, ProductUseDO productUseDO) {
        this.productDOMapper = productDOMapper;
        this.productService = productService;
        this.materialService = materialService;
        this.transportService = transportService;
        this.manufactureService = manufactureService;
        this.redisTemplate = redisTemplate;
        this.productUseDO = productUseDO;
    }

    /**
     * 获取本次计算所涉及到的所有零部件
     *
     * @return
     * @throws BusinessException
     */
        /*for (int i = 7; i >= 0; i--) {
            for (ProductInfoDO j : list) {
                String order = j.getOrderInfo();
                Integer length = order.split("\\.").length;
                if (length == i) result.add(j);
            }
        }*/
    public List<ProductInfoDO> getSonPart() throws BusinessException {
        List<ProductInfoDO> list = productService.getProductInfo(this.productUseDO.getProductId());
        if (list == null) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "查询子零件失败！");
        }
        List<ProductInfoDO> result = new ArrayList<>();
        result=sort(list,0,list.size()-1);  //对零部件的等级进行排序
        //构造一棵树 放在redis中 从小索引位置放进去
        Tree index = new Tree("root", null);
        //遍历排好序的集合
        for (int i = 0; i <result.size(); i++) {
            try{
                String arr = result.get(i).getOrderInfo();
                ProductInfoDO value = result.get(i);
                index.add(index, new Tree(arr, value));
            }catch (Exception e){
                System.out.println("构造树时发生错误！"+result.get(i).getOrderInfo()+" "+result.get(i).getNameInfo());
                throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "构造树时发生错误！"+result.get(i).getOrderInfo()+" "+result.get(i).getNameInfo());
            }

        }
        redisTemplate.opsForValue().set("caltree" + productUseDO.getId(), index);
        ArrayList<ProductInfoDO> res=new ArrayList<>();
        for(int i=result.size()-1;i>=0;i--){
            res.add(result.get(i));
        }
        return res;
    }

    /**
     * 根据产品序号快速排序
     * @param list 产品零件集合
     * @param left 排序范围的最左侧边界
     * @param right 排序范围的最右侧边界
     * @return 返回排好序的零件集合
     */
    public List<ProductInfoDO> sort(List<ProductInfoDO> list,int left,int right) {
        int povit=list.get((left+right)/2).getOrderInfo().split("\\.").length,l=left,r=right;
        while(l<r){
            while(list.get(l).getOrderInfo().split("\\.").length<povit) l++;
            while(list.get(r).getOrderInfo().split("\\.").length>povit) r--;
            if(l>=r) break;
            swap(list,r,l);
            if (list.get(r).getOrderInfo().split("\\.").length == povit) l++;
            if (list.get(l).getOrderInfo().split("\\.").length == povit) r--;
        }
        if (l == r) {
            l++;
            r--;
        }
        if (l < right) sort(list, l, right);
        if (left < r) sort(list, left, r);
        return list;
    }

    /**
     * 交换函数
     * @param list 需要修改的集合
     * @param i
     * @param j
     */
    public void swap(List<ProductInfoDO> list, int i, int j) {
        ProductInfoDO temp = list.get(i);
        ProductInfoDO t=list.get(j);
        list.set(i,t);
        list.set(j,temp);
    }




    /**
     * 计算每个零件的结果
     */
    public PartTree1 calLCA(ProductInfoDO productInfoDO) throws BusinessException {

        //获取这颗树
        Tree index = (Tree) redisTemplate.opsForValue().get("caltree" + productUseDO.getId());
        if (index == null) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "初始化零件树错误！计算失败");
        }
        PartTree1 partTree1 = new PartTree1();
        LCACalculateProperties1 util = new LCACalculateProperties1();
        //判断这棵树是叶子结点还是非叶子结点
        List<Tree> sons = index.searchSon(index, productInfoDO.getOrderInfo());


        //是叶子结点
        if (sons.size() == 0) {
            //1 原材料采掘阶段
            String name = productInfoDO.getMaterialInfo();
            BigDecimal weighttotalInfo = productInfoDO.getWeighttotalInfo();
            if (weighttotalInfo == null || weighttotalInfo.equals(new BigDecimal(0.0))) {
                BigDecimal num = new BigDecimal(productInfoDO.getNumeveryInfo());
                BigDecimal everyweight = new BigDecimal(productInfoDO.getNumeveryInfo());
                weighttotalInfo = num.multiply(everyweight);
            }
            MaterialModel material = materialService.getDetailByName(name, productInfoDO.getUserId());
            if (material == null) {
                throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "未找到相关材料，计算失败！");
            }
            LCAResult1 m = convertFrom(material);
            m = util.setProperties(m);
            LCAResult1 res1 = m.multiply(weighttotalInfo);  //原材料结果

            //2 运输阶段的结果
            String tran_name = productInfoDO.getTransportstyleInfo(); //运输方式
            Integer distanceInfo = productInfoDO.getDistanceInfo(); //运输距离
            TransportModel transportModel = transportService.getDetailByName(tran_name, productInfoDO.getUserId());
            if (transportModel == null) {
                throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "查询不到运输数据！计算失败");
            }
            LCAResult1 t = convertFrom(transportModel);
            t = util.setProperties(t);
            LCAResult1 res2 = t.multiply(new BigDecimal(distanceInfo)).multiply(weighttotalInfo);

            //3 制造阶段
            String man_name = productInfoDO.getManufacturestyleInfo();
            String[] mans = man_name.split("-");

            LCAResult1 res3 = new LCAResult1();
            for (String i : mans) {
                if (i.equals("不计算")) {
                    break;
                }
                ManufactureModel manufactureModel = manufactureService.getDetailByName(i, name, productInfoDO.getUserId());
                if (manufactureModel == null) {
                    throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "查询不到工艺数据！计算失败");
                }
                LCAResult1 ma = convertFrom(manufactureModel);
                ma = util.setProperties(ma);
                res3 = res3.add(ma.multiply(weighttotalInfo).multiply(new BigDecimal(manufactureModel.getPercent() / 100.0)));
            }


            //4 回收阶段
            Integer recycleInfo = productInfoDO.getRecycleInfo();
            Double per = recycleInfo.doubleValue() / 100.0;
            LCAResult1 res4 = res1.multiply(new BigDecimal(per)).multiply(new BigDecimal(-1));

            partTree1.setRawmaterialMiningStage(res1);
            partTree1.setPartTransportationStage(res2);
            partTree1.setManufacturingStage(res3);
            partTree1.setRecoveryStage(res4);

            //计算完之后把结果再次放入树中
            Tree search = index.search(index, productInfoDO.getOrderInfo());
            search.setResult(partTree1);
            redisTemplate.opsForValue().set("caltree" + productUseDO.getId(), index);
            redisTemplate.expire("caltree" + productUseDO.getId(),5, TimeUnit.MINUTES);
            return partTree1;
        } else {
            PartTree1 leaf = new PartTree1();
            LCAResult1 L1 = new LCAResult1();
            LCAResult1 L2 = new LCAResult1();
            LCAResult1 L3 = new LCAResult1();
            LCAResult1 L4 = new LCAResult1();

            //不是叶子结点
            //1 原材料采掘阶段
            //2 原材料运输阶段
            //3 制造阶段
            //4 回收阶段
            for (int i = 0; i < sons.size(); i++) {
                PartTree1 s = sons.get(i).getResult();//获取子节点
                L1 = L1.add(s.getRawmaterialMiningStage());
                L2 = L2.add(s.getManufacturingStage());
                L3 = L3.add(s.getRecoveryStage());
                L4 = L4.add(s.getPartTransportationStage());
            }
            //非叶结点的运输阶段
            BigDecimal weighttotalInfo = productInfoDO.getWeighttotalInfo();
            if (weighttotalInfo == null || weighttotalInfo.equals(new BigDecimal(0.0))) {
                BigDecimal num = new BigDecimal(productInfoDO.getNumeveryInfo());
                BigDecimal everyweight = new BigDecimal(productInfoDO.getNumeveryInfo());
                weighttotalInfo = num.multiply(everyweight);
            }
            String tran_name = productInfoDO.getTransportstyleInfo(); //运输方式
            Integer distanceInfo = productInfoDO.getDistanceInfo(); //运输距离
            TransportModel transportModel = transportService.getDetailByName(tran_name, productInfoDO.getUserId());
            if (transportModel == null) {
                throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "查询不到运输数据！计算失败");
            }
            LCAResult1 t = convertFrom(transportModel);
            t = util.setProperties(t);
            LCAResult1 res2 = t.multiply(new BigDecimal(distanceInfo)).multiply(weighttotalInfo);
            L4 = L4.add(res2);

            leaf.setRawmaterialMiningStage(L1);
            leaf.setPartTransportationStage(L4);
            leaf.setManufacturingStage(L2);
            leaf.setRecoveryStage(L3);
            Tree search = index.search(index, productInfoDO.getOrderInfo());
            search.setResult(leaf);
            redisTemplate.opsForValue().set("caltree" + productUseDO.getId(), index);
            redisTemplate.expire("caltree" + productUseDO.getId(),5, TimeUnit.MINUTES);
            return leaf;
        }

    }

    //计算每个产品的结果
    public ProductRoot1 calLCA(ProductDO productDO, List<PartTree1> partTree1s) throws BusinessException {

        Tree index = (Tree) redisTemplate.opsForValue().get("caltree" + productUseDO.getId());
        if (index == null) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "初始化零件树失败！计算失败");
        }

        ProductRoot1 root = new ProductRoot1();
        LCACalculateProperties1 util = new LCACalculateProperties1();
        //原材料采掘阶段
        LCAResult1 res1 = new LCAResult1();
        //零件运输阶段的结果
        LCAResult1 res2 = new LCAResult1();
        //制造阶段
        LCAResult1 res3 = new LCAResult1();
        //回收阶段
        LCAResult1 res6 = new LCAResult1();
        //使用阶段
        LCAResult1 res4 = new LCAResult1();

        //1 计算电能
        BigDecimal power = productUseDO.getPower();
        BigDecimal time = productUseDO.getTime();
        BigDecimal usenum1 = productUseDO.getUsenum();
        time=time.multiply(usenum1);
        MaterialModel material = materialService.getDetailByName("电能", productDO.getUserId());
        if (material == null) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "查询不到电能的数据！计算失败");
        }
        LCAResult1 m = convertFrom(material);  //1 kwh电能的结果
        m = util.setProperties(m);

        if (productUseDO.getTimeUnit().equals("hour")) {
            m = m.multiply(time);
        } else if (productUseDO.getTimeUnit().equals("day")) {
            m = m.multiply(time).multiply(new BigDecimal(24));
        } else if (productUseDO.getTimeUnit().equals("month")) {
            m = m.multiply(time).multiply(new BigDecimal(24 * 30));
        } else {
            m = m.multiply(time).multiply(new BigDecimal(24 * 30 * 12));
        }
        power = power.divide(new BigDecimal(1000));
        m = m.multiply(power);

        //2 计算水资源消耗
        MaterialModel water = materialService.getDetailByName("水", productDO.getUserId());
        if (water == null) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "查询不到水的数据！计算失败");
        }

        LCAResult1 w = convertFrom(water);  //1 kg水的结果
        w = util.setProperties(w);
        w = w.multiply(productUseDO.getWater());

        //3 计算汽油资源消耗
        MaterialModel gasoil = materialService.getDetailByName("汽油", productDO.getUserId());
        if (gasoil == null) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "查询不到汽油的数据！计算失败");
        }
        LCAResult1 g = convertFrom(gasoil);  //1 kg汽油的结果
        g = util.setProperties(g);
        g = g.multiply(productUseDO.getGasoline());

        //4 计算柴油资源消耗
        MaterialModel dieoil = materialService.getDetailByName("柴油", productDO.getUserId());
        if (dieoil == null) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "查询不到柴油的数据！计算失败");
        }
        LCAResult1 d = convertFrom(dieoil);  //1 kg柴油的结果
        d = util.setProperties(d);
        d = d.multiply(productUseDO.getGasoline());

        //5 计算天然气的资源消耗
        MaterialModel gas = materialService.getDetailByName("天燃气", productDO.getUserId());
        if (gas == null) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "查询不到天燃气的数据！计算失败");
        }
        LCAResult1 ga = convertFrom(gas);  //1 kg天燃气的结果
        ga = util.setProperties(ga);
        ga = ga.multiply(productUseDO.getGasoline());

        //6 获取产品使用次数
        BigDecimal usenum = productUseDO.getUsenum();

        //7 计算使用阶段的消耗
        res4 = res4.add((m.add(w).add(g).add(d).add(ga).add(res4)).multiply(usenum));


        //产品运输阶段
        Integer distance = productDO.getDistance();
        String tran_name = productDO.getAim();
        TransportModel transportModel = transportService.getDetailByName(tran_name, productDO.getUserId());
        if (transportModel == null) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "查询不到运输的数据！计算失败");
        }
        LCAResult1 tt = convertFrom(transportModel);
        tt = util.setProperties(tt);
        LCAResult1 res5 = tt.multiply(new BigDecimal(distance)).multiply(new BigDecimal(productDO.getWeight()));


        //获取根节点下的直接结点
        List<Tree> root1 = index.searchSon(index, "root");
        List<PartTree1> ts = new ArrayList<>();
        for (Tree t : root1) ts.add(t.getResult());

        //计算根节点下直接的数值
        for (PartTree1 t : ts) {
            //res2 = res2.add(t.getPartTransportationStage());
            res1 = res1.add(t.getRawmaterialMiningStage());
            res3 = res3.add(t.getManufacturingStage());
            res6 = res6.add(t.getRecoveryStage());
        }
        //计算产品原材料采掘阶段的数据
        List<PartTree1> partTree1s1 = calAllTran(root1);
        for(PartTree1 s:partTree1s1){
            res2 = res2.add(s.getPartTransportationStage());
        }


        root.setRawmaterialMiningStage(res1);
        root.setPartTransportationStage(res2);
        root.setManufacturingStage(res3);
        root.setProductUseStage(res4);
        root.setProductTransportationStage(res5);
        root.setRecoveryStage(res6);
        redisTemplate.delete("caltree" + productUseDO.getId());
        return root;
    }

    //获取产品所有子节点
    public List<PartTree1> calAllTran(List<Tree> root1){
        List<PartTree1> res=new ArrayList<>();
        for (Tree t : root1){
            addAll(res,t);
        }
        return res;
    }

    public List<PartTree1> addAll(List<PartTree1> res,Tree t){
        if(t==null) return res;
        res.add(t.getResult());
        if(t.getSuntree()==null||t.getSuntree().size()==0) return res;
        HashMap<String, Tree> suntree = t.getSuntree();
        for(Map.Entry<String,Tree> m:suntree.entrySet()){
            addAll(res,m.getValue());
        }
        return res;
    }


    private LCAResult1 convertFrom(MaterialModel materialModel) {
        LCAResult1 lcaResult1 = new LCAResult1();
        BeanUtils.copyProperties(materialModel, lcaResult1);
        return lcaResult1;
    }


    private LCAResult1 convertFrom(TransportModel materialModel) {
        LCAResult1 lcaResult1 = new LCAResult1();
        BeanUtils.copyProperties(materialModel, lcaResult1);
        return lcaResult1;
    }


    private LCAResult1 convertFrom(ManufactureModel materialModel) {
        LCAResult1 lcaResult1 = new LCAResult1();
        BeanUtils.copyProperties(materialModel, lcaResult1);
        return lcaResult1;
    }


    //计算叶子节点的PartTree
    private PartTree1 calLeaf(ProductInfoDO productInfoDO) throws BusinessException {
        PartTree1 partTree1 = new PartTree1();
        LCACalculateProperties1 util = new LCACalculateProperties1();
        //1 原材料采掘阶段
        String name = productInfoDO.getMaterialInfo();
        BigDecimal weighttotalInfo = productInfoDO.getWeighttotalInfo();
        if (weighttotalInfo == null || weighttotalInfo.equals(new BigDecimal(0.0))) {
            BigDecimal num = new BigDecimal(productInfoDO.getNumeveryInfo());
            BigDecimal everyweight = new BigDecimal(productInfoDO.getNumeveryInfo());
            weighttotalInfo = num.multiply(everyweight);
        }
        MaterialModel material = materialService.getDetailByName(name, productInfoDO.getUserId());
        if (material == null) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "查询不到材料的数据！计算失败");
        }
        LCAResult1 m = convertFrom(material);
        m = util.setProperties(m);
        LCAResult1 res1 = m.multiply(weighttotalInfo);  //原材料结果

        //2 运输阶段的结果
        String tran_name = productInfoDO.getTransportstyleInfo(); //运输方式
        Integer distanceInfo = productInfoDO.getDistanceInfo(); //运输距离
        TransportModel transportModel = transportService.getDetailByName(tran_name, productInfoDO.getUserId());
        if (transportModel == null) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "查询不到运输的数据！计算失败");
        }
        LCAResult1 t = convertFrom(transportModel);
        t = util.setProperties(t);
        LCAResult1 res2 = t.multiply(new BigDecimal(distanceInfo)).multiply(weighttotalInfo);

        //3 制造阶段
        String man_name = productInfoDO.getManufacturestyleInfo();
        String[] mans = man_name.split("-");

        LCAResult1 res3 = new LCAResult1();
        for (String i : mans) {
            ManufactureModel manufactureModel = manufactureService.getDetailByName(i, name, productInfoDO.getUserId());
            if (manufactureModel == null) {
                throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "查询不到制造工艺的数据！计算失败");
            }
            LCAResult1 ma = convertFrom(manufactureModel);
            ma = util.setProperties(ma);
            res3 = res3.add(ma.multiply(weighttotalInfo).multiply(new BigDecimal(manufactureModel.getPercent() / 100.0)));
        }


        //4 回收阶段
        Integer recycleInfo = productInfoDO.getRecycleInfo();
        Double per = recycleInfo.doubleValue() / 100.0;
        LCAResult1 res4 = res1.multiply(new BigDecimal(per)).multiply(new BigDecimal(-1));

        partTree1.setRawmaterialMiningStage(res1);
        partTree1.setPartTransportationStage(res2);
        partTree1.setManufacturingStage(res3);
        partTree1.setRecoveryStage(res4);
        return partTree1;
    }

}



